#include <stdio.h>
#include <stdlib.h>

#include "..\src\srs\srs_librtmp.h"

int read_h264_frame(char* data, int size, char** pp, int* pnb_start_code, int fps,
	char** frame, int* frame_size, int* dts, int* pts)
{
	char* p = *pp;

	// @remark, for this demo, to publish h264 raw file to SRS,
	// we search the h264 frame from the buffer which cached the h264 data.
	// please get h264 raw data from device, it always a encoded frame.
	if (!srs_h264_startswith_annexb(p, size - (p - data), pnb_start_code)) {
		srs_human_trace("h264 raw data invalid.");
		return -1;
	}

	// @see srs_write_h264_raw_frames
	// each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0,
	// for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40)
	*frame = p;
	p += *pnb_start_code;

	for (; p < data + size; p++) {
		if (srs_h264_startswith_annexb(p, size - (p - data), NULL)) {
			break;
		}
	}

	*pp = p;
	*frame_size = p - *frame;
	if (*frame_size <= 0) {
		srs_human_trace("h264 raw data invalid.");
		return -1;
	}

	// @remark, please get the dts and pts from device,
	// we assume there is no B frame, and the fps can guess the fps and dts,
	// while the dts and pts must read from encode lib or device.
	*dts += 1000 / fps;
	*pts = *dts;

	return 0;
}

int main(int argc, char** argv)
{
	const char* raw_file = "C:/SVN_PRO/LITETS/H264_STREAM/output.h264";
	const char* rtmp_url = "rtmp://192.168.2.77:1935/live/livestream";
	char* h264_raw = NULL;
	srs_rtmp_t rtmp = NULL;
	// @remark, the dts and pts if read from device, for instance, the encode lib,
	// so we assume the fps is 25, and each h264 frame is 1000ms/25fps=40ms/f.
	double fps = 25;
	srs_human_trace("raw_file=%s, rtmp_url=%s, fps=%.2f", raw_file, rtmp_url, fps);

	// open file
	FILE* raw_fd = NULL;
	errno_t err = fopen_s(&raw_fd, raw_file, "rb");
	if (err) {
		srs_human_trace("open h264 raw file %s failed.", raw_file);
		goto rtmp_destroy;
	}
	{
		fseek(raw_fd, 0, SEEK_END);
		off_t file_size = ftell(raw_fd);
		if (file_size <= 0) {
			srs_human_trace("h264 raw file %s empty.", raw_file);
			goto rtmp_destroy;
		}
		srs_human_trace("read entirely h264 raw file, size=%dKB", (int)(file_size / 1024));

		h264_raw = (char*)malloc(file_size);
		if (!h264_raw) {
			srs_human_trace("alloc raw buffer failed for file %s.", raw_file);
			goto rtmp_destroy;
		}

		fseek(raw_fd, 0, SEEK_SET);

		ssize_t nb_read = 0;
		if ((nb_read = fread(h264_raw, 1, file_size, raw_fd)) != file_size) {
			srs_human_trace("buffer %s failed, expect=%dKB, actual=%dKB.",
				raw_file, (int)(file_size / 1024), (int)(nb_read / 1024));
			goto rtmp_destroy;
		}

		// connect rtmp context
		rtmp = srs_rtmp_create(rtmp_url);

		if (srs_rtmp_handshake(rtmp) != 0) {
			srs_human_trace("simple handshake failed.");
			goto rtmp_destroy;
		}
		srs_human_trace("simple handshake success");

		if (srs_rtmp_connect_app(rtmp) != 0) {
			srs_human_trace("connect vhost/app failed.");
			goto rtmp_destroy;
		}
		srs_human_trace("connect vhost/app success");

		if (srs_rtmp_publish_stream(rtmp) != 0) {
			srs_human_trace("publish stream failed.");
			goto rtmp_destroy;
		}
		srs_human_trace("publish stream success");

		int dts = 0;
		int pts = 0;
		// @remark, to decode the file.
		char* p = h264_raw;
		int count = 0;
		for (; p < h264_raw + file_size;) {
			// @remark, read a frame from file buffer.
			char* data = NULL;
			int size = 0;
			int nb_start_code = 0;
			if (read_h264_frame(h264_raw, (int)file_size, &p, &nb_start_code, fps, &data, &size, &dts, &pts) < 0) {
				srs_human_trace("read a frame from file buffer failed.");
				goto rtmp_destroy;
			}

			// send out the h264 packet over RTMP
			int ret = srs_h264_write_raw_frames(rtmp, data, size, dts, pts);
			if (ret != 0) {
				if (srs_h264_is_dvbsp_error(ret)) {
					srs_human_trace("ignore drop video error, code=%d", ret);
				}
				else if (srs_h264_is_duplicated_sps_error(ret)) {
					srs_human_trace("ignore duplicated sps, code=%d", ret);
				}
				else if (srs_h264_is_duplicated_pps_error(ret)) {
					srs_human_trace("ignore duplicated pps, code=%d", ret);
				}
				else {
					srs_human_trace("send h264 raw data failed. ret=%d", ret);
					goto rtmp_destroy;
				}
			}

			// 5bits, 7.3.1 NAL unit syntax,
			// ISO_IEC_14496-10-AVC-2003.pdf, page 44.
			//  7: SPS, 8: PPS, 5: I Frame, 1: P Frame, 9: AUD, 6: SEI
			uint8_t nut = (char)data[nb_start_code] & 0x1f;
			srs_human_trace("sent packet: type=%s, time=%d, size=%d, fps=%.2f, b[%d]=%#x(%s)",
				srs_human_flv_tag_type2string(SRS_RTMP_TYPE_VIDEO), dts, size, fps, nb_start_code, (char)data[nb_start_code],
				(nut == 7 ? "SPS" : (nut == 8 ? "PPS" : (nut == 5 ? "I" : (nut == 1 ? "P" : (nut == 9 ? "AUD" : (nut == 6 ? "SEI" : "Unknown")))))));

			// @remark, when use encode device, it not need to sleep.
			if (count++ == 9) {
				usleep(1000 * 1000 * count / fps);
				count = 0;
			}
		}
		srs_human_trace("h264 raw data completed");
	}
rtmp_destroy:
	srs_rtmp_destroy(rtmp);
	fclose(raw_fd);
	free(h264_raw);

	return 0;
}

